package com.vf.cloud.rendering.common.factory;

import java.net.InetSocketAddress;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import com.jfinal.kit.StrKit;
import com.vf.cloud.rendering.common.pool.ProssPool;
import com.vf.cloud.rendering.common.util.SignallingUtil;
import com.vf.cloud.rendering.common.vo.Streamer;

import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UEFactory {
	
	private static volatile UEFactory INSYANCE;

	public static UEFactory getInstance() {
		if (null == INSYANCE) {
			synchronized (UEFactory.class) {
				if (null == INSYANCE) {
					INSYANCE = new UEFactory();
				}
			}
		}
		return INSYANCE;
	}
	
	private ConcurrentHashMap<String, Streamer> STREAMER_GPU = new ConcurrentHashMap<String, Streamer>();
	private ConcurrentHashMap<String, ChannelHandlerContext> UE_CLINET = new ConcurrentHashMap<String, ChannelHandlerContext>();
	private ConcurrentHashMap<String, ChannelHandlerContext> PLAYER_CLINET = new ConcurrentHashMap<String, ChannelHandlerContext>();
	private ConcurrentHashMap<String, String> BRIDGE = new ConcurrentHashMap<String, String>();
	private ConcurrentHashMap<String, String> CLIENT_TYPE = new ConcurrentHashMap<String, String>();
	private ConcurrentHashMap<String, Date> TIME_OUT = new ConcurrentHashMap<String, Date>();
	
	public void check() {
		if (UE_CLINET.size() <= 0) {
			TIME_OUT.clear();
			return;
		}
		Iterator<String> it = UE_CLINET.keySet().iterator();
		while (it.hasNext()) {
			String EIO = it.next();
			
			if(!PLAYER_CLINET.containsKey(EIO)) {
				if (TIME_OUT.containsKey(EIO)) {
					if (new Date(System.currentTimeMillis()).getTime() > TIME_OUT.get(EIO).getTime()) {
						killUEByEIO(EIO);
						TIME_OUT.remove(EIO);
						log.info(String.format("UE is Timeout,Kill:%s", EIO));
					}
				} else {
					TIME_OUT.put(EIO, getLaterTime());
					log.info(String.format("UE add To Timeout :%s",EIO));
				}
			}else {
				if (TIME_OUT.containsKey(EIO)) {
					TIME_OUT.remove(EIO);
					log.info(String.format("UE remove Timeout :%s", EIO));
				}
			}
		}
	}
	
	public void removeTimeout(String EIO) {
		TIME_OUT.remove(EIO);
	}

	private Date getLaterTime() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MINUTE, 1);
		return cal.getTime();
	}
	
	public void killUEByEIO(String EIO) {
		ChannelHandlerContext ctx = UE_CLINET.get(EIO);
		if (ctx != null) {
			InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
			String clientPort = String.valueOf(insocket.getPort());
			String pid = ProssPool.getPid(clientPort);
			if (!StrKit.isBlank(pid)) {
				ProssPool.kill(EIO, pid);
			}
		}
	}
	
	public void addEIOStreamer(String eIO, Streamer streamer) {
		STREAMER_GPU.put(eIO, streamer);
	}
	
	public void removeEIOStreamer(String eIO) {
		STREAMER_GPU.remove(eIO);
	}
	
	public Streamer getStreamerByEIO(String eIO) {
		return STREAMER_GPU.get(eIO);
	}

	public void addUE(String eIO, ChannelHandlerContext ctx) {
		UE_CLINET.put(eIO, ctx);
		BRIDGE.put(ctx.channel().id().asLongText(), eIO);
		CLIENT_TYPE.put(ctx.channel().id().asLongText(), "UE");
		SignallingUtil.onStreamerConnected(eIO);
	}

	public void addPlayer(String eIO, ChannelHandlerContext ctx) {
		PLAYER_CLINET.put(eIO, ctx);
		BRIDGE.put(ctx.channel().id().asLongText(), eIO);
		CLIENT_TYPE.put(ctx.channel().id().asLongText(), "EIO");
	}
	
	public  String getType(String channelId) {
		if (CLIENT_TYPE.containsKey(channelId)) {
			return CLIENT_TYPE.get(channelId);
		}
		return "";
	}
	
	
	public  ChannelHandlerContext getUEById(String playerId) {
		return UE_CLINET.get(playerId);
	}

	public  ChannelHandlerContext getPlayerById(String playerId) {
		return PLAYER_CLINET.get(playerId);
	}
	
	public boolean hasUEPross(String eIO) {
		return UE_CLINET.containsKey(eIO);
	}
	
	public boolean hasPlayerPross(String eIO) {
		return PLAYER_CLINET.containsKey(eIO);
	}
	
	public  String getEIOByChannelId(String channelId) {
		if (BRIDGE.containsKey(channelId))
			return BRIDGE.get(channelId);
		return "";
	}
	
	public  void removeUECache(String channelId) {
		if (BRIDGE.containsKey(channelId)) {
			killUEByEIO(BRIDGE.get(channelId));
			UE_CLINET.remove(BRIDGE.get(channelId));
			BRIDGE.remove(channelId);
		}
		CLIENT_TYPE.remove(channelId);
	}

	public  void removePlayerCache(String channelId) {
		if (BRIDGE.containsKey(channelId)) {
			killUEByEIO(BRIDGE.get(channelId));//客户端掉线立即关闭流
			PLAYER_CLINET.remove(BRIDGE.get(channelId));
			BRIDGE.remove(channelId);
		}
		CLIENT_TYPE.remove(channelId);
	}
	
	public  void KillAllUeClient() {
		Iterator<String> UE_IT = UE_CLINET.keySet().iterator();
		while (UE_IT.hasNext()) {
			String playerId=UE_IT.next();
			killUEByEIO(playerId);
		}
	}
	
	public List<Streamer> getStreamers(){
		List<Streamer> list=new LinkedList<Streamer>();
		Iterator<String> it = STREAMER_GPU.keySet().iterator();
		while (it.hasNext()) {
			String eio=it.next();
			list.add(STREAMER_GPU.get(eio));
		}
		return list;
	}

}
